﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using KinectManager;
using OpenCvSharp;
using System.Timers;
using OpenNI;

namespace DisplayInteraction1
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //readonly int IMAGE_WIDTH;
        //readonly int IMAGE_HEIGHT;

        HandsTracker _handTracker;
        Lanceur lanceur;
        string adresseLocale = "127.255.255.255:2010";

        List<Ellipse> listeEllipses = new List<Ellipse>();

        Ellipse ellipse = new Ellipse
        {
            Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0)),
            Width = 25,
            Height = 25
        };

        BackgroundWorker _bgWorker = new BackgroundWorker();

        public MainWindow()
        {
            try
            {
                InitializeComponent();

                CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

                _handTracker = new HandsTracker("SamplesConfig.xml", adresseLocale);

                _handTracker.HandFingerInfo += new HandsTracker.HandFingerInfoHandler(Hand_FingerDraw);
                _handTracker.HandContourInfo += new HandsTracker.HandContourInfoHandler(Hand_ContourDraw);
                _handTracker.Test += new HandsTracker.TestHandler(test_printMat);

                //IMAGE_WIDTH = _handTracker.Width;
                //IMAGE_HEIGHT = _handTracker.Height;

                _bgWorker.DoWork += new DoWorkEventHandler(Worker_DoWork);

                lanceur = new Lanceur(this);
                lanceur.Show();
            }
            catch
            {
                Console.WriteLine("ERREUR KINECT : la kinect n'est pas branchée ou le fichier de configuration \"SamplesConfig.xml\" est introuvable.");
                MessageBox.Show("ERREUR KINECT : la kinect n'est pas branchée ou le fichier de configuration \"SamplesConfig.xml\" est introuvable.");
                Application.Current.Shutdown();
            }
            //test();
        }

        public void majFeedbackConnection(bool displayReady, bool userReady)
        {
            Uri urlImage;
            BitmapImage sourceImage;

            if (displayReady) urlImage = new Uri("../Images/ecran.png", UriKind.Relative);
            else urlImage = new Uri("../Images/no_ecran.png", UriKind.Relative);
            sourceImage = new BitmapImage(urlImage);
            imageDisplay.Source = sourceImage;

            if (userReady) urlImage = new Uri("../Images/user.png", UriKind.Relative);
            else
            {
                urlImage = new Uri("../Images/no_user.png", UriKind.Relative);
                majMainDroite(null, 0, 0);
            }
            sourceImage = new BitmapImage(urlImage);
            imageUser.Source = sourceImage;
        }

        public void majMainDroite(String description, int x, int y)
        {
            if (description == null)
            {
                imageMainDroite.Visibility = Visibility.Hidden;
            }
            else
            {
                imageMainDroite.Visibility = Visibility.Visible;
                Uri urlImage;
                BitmapImage sourceImage;
                switch (description)
                {
                    case "OPENED":
                        urlImage = new Uri("../Images/free.png", UriKind.Relative);
                        break;
                    case "CLOSED":
                        urlImage = new Uri("../Images/selected.png", UriKind.Relative);
                        break;
                    case "FINGER_ON":
                        urlImage = new Uri("../Images/fingerOn.png", UriKind.Relative);
                        break;
                    case "FINGER_OFF":
                        urlImage = new Uri("../Images/fingerOff.png", UriKind.Relative);
                        break;
                    default:
                        urlImage = new Uri("../Images/free.png", UriKind.Relative);
                        break;
                }
                sourceImage = new BitmapImage(urlImage);
                imageMainDroite.Source = sourceImage;
                Canvas.SetTop(imageMainDroite, y + imageMainDroite.Height / 2);
                Canvas.SetLeft(imageMainDroite, x - imageMainDroite.Width / 2 + 14);
            }
        }

        void Hand_FingerDraw(object sender, List<Point> l1, Color c1, List<Point> l2, Color c2)
        {
            resetListeEllipses();
            //Console.WriteLine("Go print !");
            Dispatcher.BeginInvoke((Action)delegate
            {
                for (int i = 0; i < l1.Count(); i++)
                {
                    Ellipse ellipse = new Ellipse
                    {
                        Fill = new SolidColorBrush(c1),
                        Width = 20,
                        Height = 20
                    };

                    Canvas.SetLeft(ellipse, l1[i].Y - 10 - 20);
                    Canvas.SetBottom(ellipse, 480 - l1[i].X - 10 - 20);

                    listeEllipses.Add(ellipse);
                    canvas.Children.Add(ellipse);
                }

                if (l2 != null)
                {

                    for (int i = 0; i < l2.Count(); i++)
                    {
                        Ellipse ellipse = new Ellipse
                        {
                            Fill = new SolidColorBrush(c2),
                            Width = 20,
                            Height = 20
                        };

                        Canvas.SetLeft(ellipse, l2[i].Y - 10 - 25);
                        Canvas.SetBottom(ellipse, 480 - l2[i].X - 10 - 25);

                        listeEllipses.Add(ellipse);
                        canvas.Children.Add(ellipse);
                    }
                }
            });
        }

        #region private methods

        private void test()
        {
            CvMat mask = new CvMat(640, 480, 0);
            mask.Zero();

            // extract hand region	
            Cv.Circle(mask, new CvPoint(300, 200), 100, (CvScalar)255, -1);
            //Cv.Circle(mask, new CvPoint(200, 420), 50, (CvScalar)255, -1);
            //Cv.Circle(mask, new CvPoint(70, 120), 50, (CvScalar)255, -1);
            test_printMat(null, mask);

            List<List<Point>> contours = getContours(mask);
            Console.WriteLine("Nb contours : " + contours.Count());

            int longestIndex = getLongestListIndex(contours);
            /*for (int i = 0; i < contours.Count(); i++)
            {
                if (i == longestIndex) Hand_ContourDraw(null, contours.ElementAt(i), Colors.Red);
                else Hand_ContourDraw(null, contours.ElementAt(i), Colors.Green);
            }*/

            CvSeq<CvPoint> contour;
            CvMemStorage storage = new CvMemStorage();
            Cv.FindContours(mask, storage, out contour, CvContour.SizeOf, ContourRetrieval.External, ContourChain.ApproxSimple);

            drawList(contours.ElementAt(longestIndex));

            CvSeq<CvPoint> contour2 = ListToCvSeq(contours.ElementAt(longestIndex));

            //drawSeq(contour);
            drawSeq(contour2);

            Console.WriteLine("Taille contour : " + contour.Count());
            Console.WriteLine("Taille contour2 : " + contour2.Count());
            CvSeq<CvPoint> contoursSimplified1 = Cv.ApproxPoly(contour, CvContour.SizeOf, new CvMemStorage(), ApproxPolyMethod.DP, 2, true);
            CvSeq<CvPoint> contoursSimplified2 = Cv.ApproxPoly(contour2, CvContour.SizeOf, new CvMemStorage(), ApproxPolyMethod.DP, 2, true);
            Console.WriteLine("Taille contour simplified : " + contoursSimplified1.Count());
            Console.WriteLine("Taille contour simplified2 : " + contoursSimplified2.Count());
        }

        private int getLongestListIndex(List<List<Point>> l)
        {
            if (l.Count == 0) return -1;
            int index = 0;
            int max = l.ElementAt(0).Count();
            for (int i = 1; i < l.Count(); i++)
            {
                if (l.ElementAt(i).Count() > max)
                {
                    index = i;
                    max = l.ElementAt(i).Count();
                }
            }
            return index;
        }


        private void drawList(List<Point> l)
        {
            //if (c.Equals(Colors.Blue)) resetListeEllipses();
            //Console.WriteLine("Go print !");
            Dispatcher.BeginInvoke((Action)delegate
            {
                for (int i = 0; i < l.Count(); i++)
                {
                    Ellipse ellipse;
                    if (i <= 255)
                    {
                        ellipse = new Ellipse
                        {
                            Fill = new SolidColorBrush(Color.FromRgb((byte)i, 0, 0)),
                            Width = 5,
                            Height = 5
                        };
                    }
                    else
                    {
                        ellipse = new Ellipse
                        {
                            Fill = new SolidColorBrush(Color.FromRgb(255, (byte)i, 255)),
                            Width = 5,
                            Height = 5
                        };
                    }

                    Canvas.SetLeft(ellipse, l[i].Y - 2.5);
                    Canvas.SetBottom(ellipse, 480 - l[i].X - 2.5);

                    listeEllipses.Add(ellipse);
                    canvas.Children.Add(ellipse);
                }
            });
        }

        private void drawSeq(CvSeq<CvPoint> s)
        {
            //if (c.Equals(Colors.Blue)) resetListeEllipses();
            //Console.WriteLine("Go print !");
            Dispatcher.BeginInvoke((Action)delegate
            {
                for (int i = 0; i < s.Count(); i++)
                {
                    Ellipse ellipse;
                    if (i <= 255)
                    {
                        ellipse = new Ellipse
                        {
                            Fill = new SolidColorBrush(Color.FromRgb((byte)i, 0, 0)),
                            Width = 5,
                            Height = 5
                        };
                    }
                    else
                    {
                        ellipse = new Ellipse
                        {
                            Fill = new SolidColorBrush(Color.FromRgb(255, (byte)i, 255)),
                            Width = 5,
                            Height = 5
                        };
                    }

                    Canvas.SetLeft(ellipse, ((CvPoint)s.ElementAt(i)).Y - 2.5);
                    Canvas.SetBottom(ellipse, 480 - ((CvPoint)s.ElementAt(i)).X - 2.5);

                    listeEllipses.Add(ellipse);
                    canvas.Children.Add(ellipse);
                }
            });
        }

        private List<List<Point>> getContours(CvMat m)
        {
            List<List<Point>> contours = new List<List<Point>>();
            for (int i = 1; i < m.Rows; i++)
            {
                for (int j = 1; j < m.Cols; j++)
                {
                    if (m[i, j] == 255)
                    {
                        if (isContourPoint(m, i, j))
                        {
                            if (!listOfListContainPoint(contours, new Point(i, j)))
                            {
                                contours.Add(findContour(m, i, j));
                            }
                        }
                    }
                }
            }
            return contours;
        }

        private List<Point> findContour(CvMat m, int i, int j)
        {
            List<Point> res = new List<Point>();
            Point nextPoint = new Point(i,j);
            do
            {
                res.Add(nextPoint);
                nextPoint = nextContourPoint(m, (int)nextPoint.X, (int)nextPoint.Y, res);
            } while (!nextPoint.Equals(new Point(-1,-1)));
            return res;
        }

        private Point nextContourPoint(CvMat m, int i, int j, List<Point> l) 
        {
            if (m[i, j + 1] == 255 && isContourPoint(m,i,j + 1) && !l.Contains(new Point(i, j + 1))) return new Point(i, j + 1);
            else if (m[i + 1, j + 1] == 255 && isContourPoint(m, i + 1, j + 1) && !l.Contains(new Point(i + 1, j + 1))) return new Point(i + 1, j + 1);
            else if (m[i + 1, j] == 255 && isContourPoint(m, i + 1, j) && !l.Contains(new Point(i + 1, j))) return new Point(i + 1, j);
            else if (m[i + 1, j - 1] == 255 && isContourPoint(m, i + 1, j - 1) && !l.Contains(new Point(i + 1, j - 1))) return new Point(i + 1, j - 1);
            else if (m[i, j - 1] == 255 && isContourPoint(m, i, j - 1) && !l.Contains(new Point(i, j - 1))) return new Point(i, j - 1);
            else if (m[i - 1, j - 1] == 255 && isContourPoint(m, i - 1, j - 1) && !l.Contains(new Point(i - 1, j - 1))) return new Point(i - 1, j - 1);
            else if (m[i - 1, j] == 255 && isContourPoint(m, i - 1, j) && !l.Contains(new Point(i - 1, j))) return new Point(i - 1, j);
            else if (m[i - 1, j + 1] == 255 && isContourPoint(m, i - 1, j + 1) && !l.Contains(new Point(i - 1, j + 1))) return new Point(i - 1, j + 1);
            else return new Point(-1,-1);
        }

        private bool isContourPoint(CvMat m, int i, int j)
        {
            bool noirProche = false;
            bool blancProche = false;
            if (m[i - 1, j] == 255) noirProche = true;
            else blancProche = true;
            if (m[i + 1, j] == 255) noirProche = true;
            else blancProche = true;
            if (m[i, j + 1] == 255) noirProche = true;
            else blancProche = true;
            if (m[i, j - 1] == 255) noirProche = true;
            else blancProche = true;
            return (noirProche && blancProche);
        }

        private bool listOfListContainPoint(List<List<Point>> l, Point p)
        {
            for (int i = 0; i < l.Count(); i++)
            {
                if (l.ElementAt(i).Contains(p)) return true;
            }
            return false;
        }

        private CvSeq<CvPoint> ListToCvSeq(List<Point> l)
        {
            CvSeq<CvPoint> res = new CvSeq<CvPoint>(SeqType.Contour, new CvMemStorage());
            foreach (Point p in l)
            {
                res.Insert(0, new CvPoint((int)p.Y,(int)p.X));
            }
            return res;
        }

        private void Close(object sender, CancelEventArgs e)
        {
            if (lanceur != null) lanceur.stop();
            Environment.Exit(0);
        }


        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (!_bgWorker.IsBusy)
            {
                _bgWorker.RunWorkerAsync();
            }
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            Dispatcher.BeginInvoke((Action)delegate
            {
                imgCamera.Source = _handTracker.CameraImage;
            });
        }

        private void Hand_ContourDraw(object sender, List<Point> l, Color c)
        {
            if (c.Equals(Colors.Blue)) resetListeEllipses();
            //Console.WriteLine("Go print !");
            Dispatcher.BeginInvoke((Action)delegate
            {
                for (int i = 0; i < l.Count(); i++)
                {
                    Ellipse ellipse = new Ellipse
                    {
                        Fill = new SolidColorBrush(c),
                        Width = 5,
                        Height = 5
                    };

                    Canvas.SetLeft(ellipse, l[i].Y - 2.5);
                    Canvas.SetBottom(ellipse, 480 - l[i].X - 2.5);

                    listeEllipses.Add(ellipse);
                    canvas.Children.Add(ellipse);
                }
                /*Ellipse ellipse2 = new Ellipse
                {
                    Fill = new SolidColorBrush(Color.FromRgb(255, 0, 0)),
                    Width = 5,
                    Height = 5
                };

                Canvas.SetLeft(ellipse2, l[l.Count() - 1].Y - 2.5);
                Canvas.SetBottom(ellipse2, 480 - l[l.Count() - 1].X - 2.5);

                listeEllipses.Add(ellipse2);
                canvas.Children.Add(ellipse2);*/
            });
        }

        void test_printMat(object sender, CvMat m)
        {
            resetListeEllipses();
            Dispatcher.BeginInvoke((Action)delegate
            {
                //Console.WriteLine("PRINT M");
                for (int i = 1; i < m.Rows; i+=5)
                {
                    for (int j = 1; j < m.Cols; j+=5)
                    {
                        if (m[i, j] != 0)
                        {
                            Ellipse ellipse = new Ellipse
                            {
                                Fill = new SolidColorBrush(Color.FromRgb(0, 0, 0)),
                                Width = 5,
                                Height = 5
                            };

                            Canvas.SetTop(ellipse, j - 2.5);
                            Canvas.SetLeft(ellipse, i - 2.5);

                            listeEllipses.Add(ellipse);
                            canvas.Children.Add(ellipse);
                        }
                    }
                }
              // Console.WriteLine("M PRINTED");
            });
        }

        private void printSkeleton(object sender, SkeletonJointPosition rightHand, SkeletonJointPosition leftHand, SkeletonJointPosition leftElbow, bool rightHandOpened)
        {
            resetListeEllipses();
            Dispatcher.BeginInvoke((Action)delegate
            {
                drawDisque(leftElbow.Position, Colors.Black, 10);

                drawDisque(leftHand.Position, Colors.Black, 10);

                if (rightHandOpened) drawDisque(rightHand.Position, Colors.Green, 10);
                else drawDisque(rightHand.Position, Colors.Red, 10);
            });
        }

        private void drawDisque(Point3D pos, Color c, int r)
        {
            Ellipse ellipse = new Ellipse
            {
                Fill = new SolidColorBrush(c),
                Width = (2 * r),
                Height = (2 * r)
            };

            Canvas.SetBottom(ellipse, Height - pos.Y - r);
            Canvas.SetLeft(ellipse, pos.X - r);

            listeEllipses.Add(ellipse);
            canvas.Children.Add(ellipse);
        }

        private void resetListeEllipses()
        {
            Dispatcher.BeginInvoke((Action)delegate
            {
                for (int i = 0; i < listeEllipses.Count(); i++)
                {
                    canvas.Children.Remove(listeEllipses[i]);
                }
                listeEllipses = new List<Ellipse>();
            });
        }

        #endregion
    }
}
